﻿using System;
using System.Collections.Generic;
using DependencyAnalysis;
using DependencyAnalysis.DataModel;
using DependencyAnalysis.Internal;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTests
{
    [TestClass]
    public class VerifyVersatilityTest
    {
        private IVerifier _verifier;
        private IDependencyModel _dependencyModel;

        [TestInitialize]
        public void Initialize()
        {
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(1, 2, true, true);
            _dependencyModel = new DependencyModel();
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(1, 3, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(2, 3, true, true);
            _dependencyModel.Add(descriptor);
            _verifier = new Verifier(_dependencyModel);
        }

        [TestMethod]
        public void WhenTypeNotFoundVerifyVersatilityThrowsDependencyException()
        {
            Assert1.Throws<DependencyException>(() => _verifier.VerifyVersatility(new TypeDescriptor { Identifier = "Dummy.WillNotBeFound" }));
        }

        [TestMethod]
        public void WhenTypeIsReferredByTwoOthersVerfifyVersatilityThrowsNoException()
        {
            Assert1.DoesNotThrow<DependencyException>(
                () => _verifier.VerifyVersatility(new TypeDescriptor { Identifier = "Module3.Submodule3.Type3" }));
        }

        [TestMethod]
        public void WhenTypeIsReferredBySingleOtherVerfifyVersatilityThrowsException()
        {
            Assert1.Throws<DependencyException>(
                () => _verifier.VerifyVersatility(new TypeDescriptor { Identifier = "Module2.Submodule2.Type2" }));
        }

        [TestMethod]
        public void WhenTypeIsNotReferencedVerifyVersatilityThrowsException()
        {
            Assert1.Throws<DependencyException>(() => _verifier.VerifyVersatility(new TypeDescriptor { Identifier = "Module1.Submodule1.Type1" }));
        }

        [TestMethod]
        public void WhenTypeIsReferredBySeveralMembersOfSingleTypeVerifyVersatilityThrowsException()
        {
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 5, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 5, true, true);
            descriptor.Source.Name = "NewMember";
            descriptor.Source.FullName = "Module4.Submodule4.Type4.NewMember";
            descriptor.Source.Identifier = "Module4.Submodule4.Type4.NewMember";
            _dependencyModel.Add(descriptor);
            Assert1.Throws<DependencyException>(() => _verifier.VerifyVersatility(new TypeDescriptor { Identifier = "Module5.Submodule5.Type5" }));
        }

        [TestMethod]
        public void WhenMultipleViolationsAreFoundThenMultipleTypesAreReported()
        {
            DependencyException exception = null;
            try
            {
                _verifier.VerifyVersatilityForProject(new List<IDescriptor>());
            }
            catch (DependencyException ex)
            {
                exception = ex;
            }
            Assert.IsNotNull(exception);
            var messages = exception.Message.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            Assert.AreEqual(2, messages.Length);
        }

        [TestMethod]
        public void WhenAllowedExceptionsIsNullThenThrowArgumentNullException()
        {
            Assert1.Throws<ArgumentNullException>(() => _verifier.VerifyVersatilityForProject(null));
        }

        [TestMethod]
        public void WhenTypeIsAllowedExceptionThenTypeIsNotReported()
        {
            DependencyException exception = null;
            try
            {
                _verifier.VerifyVersatilityForProject(new List<IDescriptor>
                    {
                        new TypeDescriptor {Identifier = "Module1.Submodule1.Type1"}
                    });
            }
            catch (DependencyException ex)
            {
                exception = ex;
            }
            Assert.IsNotNull(exception);
            var messages = exception.Message.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            Assert.AreEqual(1, messages.Length);
        }

        [TestMethod]
        public void WhenAllTypesAreAllowedExceptionThenNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() => _verifier.VerifyVersatilityForProject(
                new List<IDescriptor>
                    {
                        new TypeDescriptor {Identifier = "Module1.Submodule1.Type1"},
                        new TypeDescriptor {Identifier = "Module2.Submodule2.Type2"}
                    }));
        }

        [TestMethod]
        public void WhenSubmoduleIsAllowedExceptionThenTypesOfSubmoduleAreNotReported()
        {
            DependencyException exception = null;
            try
            {
                _verifier.VerifyVersatilityForProject(new List<IDescriptor>
                    {
                        new SubmoduleDescriptor {Identifier = "Module1.Submodule1"}
                    });
            }
            catch (DependencyException ex)
            {
                exception = ex;
            }
            Assert.IsNotNull(exception);
            var messages = exception.Message.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            Assert.AreEqual(1, messages.Length);
        }

        [TestMethod]
        public void WhenAllSubmodulesAreAllowedExceptionsThenNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() => _verifier.VerifyVersatilityForProject(
                new List<IDescriptor>
                    {
                        new SubmoduleDescriptor {Identifier = "Module1.Submodule1"},
                        new SubmoduleDescriptor {Identifier = "Module2.Submodule2"}
                    }));
        }

        [TestMethod]
        public void WhenModuleIsAllowedExceptionThenTypesOfModuleAreNotReported()
        {
            DependencyException exception = null;
            try
            {
                _verifier.VerifyVersatilityForProject(new List<IDescriptor> { new ModuleDescriptor { Identifier = "Module1" } });
            }
            catch (DependencyException ex)
            {
                exception = ex;
            }
            Assert.IsNotNull(exception);
            var messages = exception.Message.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            Assert.AreEqual(1, messages.Length);
        }

        [TestMethod]
        public void WhenAllModuleAreAllowedExceptionsThenNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() => _verifier.VerifyVersatilityForProject(
                new List<IDescriptor>
                    {
                        new ModuleDescriptor {Identifier = "Module1"},
                        new ModuleDescriptor {Identifier = "Module2"}
                    }));
        }

        [TestMethod]
        public void WhenTypeAndSubmoduleOfTypeAreAllowedExceptionThenTypeIsNotReported()
        {
            DependencyException exception = null;
            try
            {
                _verifier.VerifyVersatilityForProject(
                    new List<IDescriptor>
                        {
                            new SubmoduleDescriptor {Identifier = "Module2.Submodule2"},
                            new TypeDescriptor {Identifier = "Module2.Submodule2.Type2"}
                        });
            }
            catch (DependencyException ex)
            {
                exception = ex;
            }
            Assert.IsNotNull(exception);
            var messages = exception.Message.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            Assert.AreEqual(1, messages.Length);
        }

        [TestMethod]
        public void WhenTypeAndSubmoduleAreAllowedExceptionsCoveringAllTypesThenNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() => _verifier.VerifyVersatilityForProject(
                new List<IDescriptor>
                    {
                        new TypeDescriptor {Identifier = "Module1.Submodule1.Type1"},
                        new SubmoduleDescriptor {Identifier = "Module2.Submodule2"}
                    }));
        }

        [TestMethod]
        public void WhenTypeAndModuleAreAllowedExceptionsCoveringAllTypesThenNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() => _verifier.VerifyVersatilityForProject(
                new List<IDescriptor>
                    {
                        new ModuleDescriptor {Identifier = "Module1"},
                        new TypeDescriptor {Identifier = "Module2.Submodule2.Type2"}
                    }));
        }

        [TestMethod]
        public void WhenModuleAndSubmoduleAreAllowedExceptionsCoveringAllTypesThenNoExceptionIsThrown()
        {
            Assert1.DoesNotThrow<DependencyException>(() => _verifier.VerifyVersatilityForProject(
                new List<IDescriptor>
                    {
                        new ModuleDescriptor {Identifier = "Module1"},
                        new SubmoduleDescriptor {Identifier = "Module2.Submodule2"}
                    }));
        }

        [TestMethod]
        public void WhenTypeAndModuleAreAllowedExceptionThenTypeIsNotReported()
        {
            DependencyException exception = null;
            try
            {
                _verifier.VerifyVersatilityForProject(
                    new List<IDescriptor>
                        {
                            new ModuleDescriptor {Identifier = "Module1"},
                            new TypeDescriptor {Identifier = "Module1.Submodule1.Type1"}
                        });
            }
            catch (DependencyException ex)
            {
                exception = ex;
            }
            Assert.IsNotNull(exception);
            var messages = exception.Message.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            Assert.AreEqual(1, messages.Length);
        }

        [TestMethod]
        public void WhenModuleAndSubmoduleAreAllowedExceptionThenTypeIsNotReported()
        {
            DependencyException exception = null;
            try
            {
                _verifier.VerifyVersatilityForProject(
                    new List<IDescriptor>
                        {
                            new ModuleDescriptor {Identifier = "Module1"},
                            new SubmoduleDescriptor {Identifier = "Module1.Submodule1"}
                        });
            }
            catch (DependencyException ex)
            {
                exception = ex;
            }
            Assert.IsNotNull(exception);
            var messages = exception.Message.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            Assert.AreEqual(1, messages.Length);
        }
    }
}
